class XML
  # blank slate
  instance_methods.each { |m| undef_method m unless (m =~ /^__|instance_eval$/)}
  
  # Each item in @doc is an array containing three values: type, value, attributes
  
  def initialize(&block)
    @doc = []
    instance_eval(&block)
  end
  
  def method_missing(tag, *args, &block)
    value = block ? nil : args.pop
    attributes = args.pop
    @__to_s = nil # invalidate to_s cache
    if value
      @doc << [:open, tag, attributes] << [:value, value] << [:close, tag]
    else
      @doc << [:open, tag, attributes]
      instance_eval(&block)
      @doc << [:close, tag]
    end
  end
  
  def instruct!(atts = nil)
    @doc << [:instruct, atts || {:version => "1.0", :encoding => "UTF-8"}]
  end

  def to_s
    @__to_s ||= @doc.map{|i| _fmt_part i}.join
  end
  
  alias_method :inspect, :to_s
  
  def _fmt_part(part)
    case part[0]
    when :instruct
      "<?xml #{_fmt_atts(part[1])}?>"
    when :open
      part[2] ? "<#{part[1]} #{_fmt_atts(part[2])}>" :
        "<#{part[1]}>"
    when :close
      "</#{part[1]}>"
    when :value
      part[1].to_s
    end
  end

  def _fmt_atts(atts)
    atts.inject([]) {|m, i| m << "#{i[0]}=#{i[1].to_s.inspect}"}.join(' ')
  end
end

def t1(list)
  xml = XML.new do
    instruct!
      reality(:time => Time.now) do
      event do
        type 1
        path "/sharon"
        value "abcdefg"
      end
      list.each {|i| item i}
    end
  end
  xml.to_s
end

require 'rubygems'
require 'builder'

def t2(list)
  xml = Builder::XmlMarkup.new(:indent => 2)
  xml.instruct!
  xml.reality(:time => Time.now) do
    xml.event do
      xml.type 1
      xml.path "/sharon"
      xml.value "abcdefg"
    end
    list.each {|i| xml.item i}
  end
  xml.to_s
end

puts "**************"
puts t1([])
puts "**************"
puts t2([])
puts "**************"

require 'benchmark'

n = 1000
list = []#(1..10).map {rand(76564).to_s(36)}
Benchmark.bm do |x|
  x.report('quick&dirty') {n.times {t1(list)}}
  x.report('builder') {n.times {t2(list)}}
end
__END__

class EventsController < ServerSide::Controller::Base.mount('/events/:format')
  invoke_by_parameter :format, :default => :html
  
  def before_response
    @events = Events.select
  end
  
  def xml
    render_xml('text/xml+svg') do
      events @events.to_xml
    end
  end
end

xml = XML.new do
  instruct!
  reality(:time => Time.now) do
    event do
      type 1
      path "/sharon"
      value "abcdefg"
    end
    list.each {|i| item i}
  end
end
xml.to_s
